<?php

/**
 * QPHP
 *
 * Copyright (c) 2022 QPHP
 *
 * @author lbnnbs
 * @copyright Copyright (c) 2022 lbnnbs QPHP (https://gitee.com/lbnnbs/qphp)
 * @license AGPLv3 License
 *
 * 函数库
 */

/**
 * 设置或者获取当前语言
 *
 * @param string $lang
 * @return string
 */
function lang($lang = '') {
    static $language;
    if ($lang) {
        $language = $lang;
    } else {
        $lang = $language;
        if (!$lang) {
            $lang = 'zh_cn';
            $language = $lang;
        }
        return $lang;
    }
}

/**
 * 按语言设置解析文字
 */
function L($paths, $args = array(), $lang = '') {
    if (!$lang) {
        $lang = lang();
    }
    
    $ret = include(ROOT_PATH . '/Lang/' . $lang . '.php');
    
    $keys = explode('/', $paths);
    foreach ($keys as $key) {
        if (!$ret = $ret[$key]) {
            return false;
        }
    }
    if ($args) {
        $ret = vsprintf($ret, $args);
    }
    return $ret;
}

function MEncode($str) {
    return Mcrypt::encrypt($str, Sys::getCfg('base_secret_key'));
}

function MDecode($str) {
    if (!$str) {
        return '';
    }
    try {
        return Mcrypt::decrypt($str, Sys::getCfg('base_secret_key'));
    } catch (\RuntimeException $ex) {
        return '';
    }
}

/**
 * 显示消息带换行和时间
 *
 * @param $message
 */
function eol($message) {
    if (!is_string($message)) {
        $message = print_r($message, true);
    }
    echo "---------------[" . date('Y-m-d H:i:s') . "]---------------" . PHP_EOL . $message . PHP_EOL;
}

/**
 * 按行显示html
 *
 * @param $message
 */
function print_br($message) {
    if (!is_string($message)) {
        $message = print_r($message, true);
    }
    echo $message . '<br />';
}


/**
 * 按日期生成目录
 *
 * @param string $base_dir 基础路径
 * @param bool $mkdir 是否在磁盘创建目录
 * @return string 返回生成的目录
 */
function date_path($base_dir, $mkdir = true) {
    
    $dir = rtrim($base_dir, '/') . '/' . date('Y/m/d') . '/';
    
    if ($mkdir && !file_exists($dir)) {
        mkdir($dir, 0775, true);
    }
    
    return $dir;
}

/**
 * 按日期生成文件路径
 *
 * @param string $base_dir 基础路径
 * @param string $ext 文件扩展名
 * @param bool $mkdir 是否在磁盘创建目录
 * @return string 返回生成的文件路径
 */
function file_date_path($base_dir, $ext = '', $mkdir = true) {
    
    return date_path($base_dir, $mkdir) . md5(uniqid(time() . rand())) . $ext;
    
}

/**
 * 按日期生成文件URL路径
 *
 * @param string $base_dir 基础路径
 * @param string $ext 文件扩展名
 * @return string 返回生成的URL路径
 */
function url_date_path($base_dir, $ext = '') {
    
    return file_date_path($base_dir, $ext, false);
    
}

/**
 * 写日志,日志都写在 Data/Logs 目录下
 *
 * @param string $message 日志内容
 * @param string $type 日志类型
 */
function wlog($message, $type = 'user') {
    // 得到日志目录
    $type = trim($type, '/');
    $path = DATA_PATH . "/Logs/{$type}/" . date('Y-m');
    if (!file_exists($path)) {
        @mkdir($path, 0775, true);
    }
    
    if (!is_string($message)) {
        $message = print_r($message, true);
    }
    
    // 得到日志文件
    $file = $path . '/' . date('Y_m_d') . '.log';
    $msg = "---------------[" . date('Y-m-d H:i:s') . "]---------------" . PHP_EOL . $message . PHP_EOL;
    file_put_contents($file, $msg, FILE_APPEND);
}

/**
 * 数组预处理，以便输出为json时有更好的数据格式
 *
 * 小数去除右侧的0值
 * null处理为空字符串
 * 小于11位的字符型数字转换为数值
 *
 * @return array
 */
function array_pretreat(&$array) {
    if (is_array($array)) {
        foreach ($array as &$value) {
            if (is_array($value)) {
                array_pretreat($value);
            } else {
                if (is_numeric($value)) {
                    // mysql int 默认11位，excel超过11位也会用科学计数法表示
                    if ($value < 99999999999) {
                        if (substr($value, 0, 1) != '0') {
                            $value = floatval($value);
                        } else {
                            if (substr($value, 0, 2) == '0.') {
                                $value = floatval($value);
                            } else {
                                continue;
                            }
                        }
                    }
                }
                if (is_null($value)) {
                    $value = '';
                }
            }
        }
    }
    return $array;
}

/**
 * 递归合并两个数组
 *
 * @param array $array1 引用传递，会被修改
 * @param array $array2
 * @return array 合并后数组
 */
function array_merge_deep(&$array1, $array2) {
    foreach ($array2 as $key => $item) { //通过键值循环
        if (is_array($item)) { //如果待合并元素同样为数组，进行深度合并
            if (isset($array1[$key]) && is_array($array1[$key])) { //如果原数组同键名对应元素同样为数组
                array_merge_deep($array1[$key], $item); //递归深度合并
            } else {//如果原数组同键名对应元素不是数组，直接覆盖
                $array1[$key] = $item;
            }
        } else { //如果待合并元素非数组，直接通过键名赋值
            $array1[$key] = $item;
        }
    }
    return $array1;
}

/**
 * 递归解码URL
 *
 * 该方法不会出现多次解析导致参数部分的url被解码的问题
 */
function http_url_decode($url) {
    if (!$url) {
        return '';
    }
    
    if (strpos($url, 'https') === 0) {
        $http = 'https';
    } else if (strpos($url, 'http') === 0) {
        $http = 'http';
    } else {
        return urldecode($url);
    }
    
    if (strpos($url, $http . '://') === 0) {
        return $url;
    } else {
        return http_url_decode(urldecode($url));
    }
}

/**
 * 任意多个变量的调试输出
 *
 * @param mixed [$var1,$var2,$var3,...]
 */
function dump() {
    if (PHP_SAPI != 'cli') {
        header("Content-Type:text/html; charset=" . Sys::getCfg('charset'));
    }
    foreach (func_get_args() as $var) {
        Sys::dump($var);
    }
    exit;
}

/**
 * 返回指间区间内的一个随机数,如果没有指定则返回 0- RAND_MAX 中取一个乱数
 */
function randNum($min = null, $max = null) {
    if ($min === null || $max === null)
        return mt_rand();
    else
        return mt_rand($min, $max);
}

/**
 * 产生随机字串,默认长度6位,字母和数字混合
 *
 * @param string $len 长度
 * @param string $type 随机符类型 0:数字 1:小写字母 2:大写字母 3:所有字母与数字组合 4:小写字母与数字组合
 * @param string $addChars 额外字符
 * @return string
 */
function randString($len = 6, $type = 3, $addChars = '') {
    $str = '';
    switch ($type) {
        case 0:
            $chars = str_repeat('0123456789', 3) . $addChars;
            break;
        case 1:
            $chars = 'abcdefghijklmnopqrstuvwxyz' . $addChars;
            break;
        case 2:
            $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . $addChars;
            break;
        case 3:
            //去掉了容易混淆的字符oOLl和数字01
            $chars = 'ABCDEFGHIJKMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789' . $addChars;
            break;
        case 4:
            //去掉了容易混淆的字符ol和数字01，仅小写
            $chars = 'abcdefghijkmnpqrstuvwxyz23456789' . $addChars;
            break;
    }
    //位数过长重复字符串一定次数
    if ($len > 10) {
        $chars = $type === 0 ? str_repeat($chars, $len) : str_repeat($chars, 5);
    }
    //得到随机数
    $charLen = strlen($chars) - 1;
    for ($i = 0; $i < $len; $i++) {
        $n = randNum(0, $charLen);
        $str .= $chars[$n];
    }
    return $str;
}

/**
 * 计算中英文混合字符串的长度
 *
 * @param string $str
 * @return int
 */
function cstrlen($str) {
    return strlen(preg_replace('/\s/', '', preg_replace('/[\x80-\xff]{1,3}/', 'a', $str)));
}

/**
 * 字符串截取，支持中文和其他编码
 *
 * @param string $str 需要转换的字符串
 * @param string $start 开始位置
 * @param string $length 截取长度
 * @param string $suffix 截断显示字符,是否显示 '...'
 * @param string $charset 编码格式
 * @return string
 */
function msubstr($str, $start = 0, $length = 0, $suffix = true, $charset = "utf-8") {
    if (function_exists("mb_substr")) {
        $strlen = mb_strlen($str, $charset);
        $slice = mb_substr($str, $start, $length, $charset);
    } elseif (function_exists('iconv_substr')) {
        $strlen = iconv_strlen($str, $charset);
        $slice = iconv_substr($str, $start, $length, $charset);
    } else {
        $re['utf-8'] = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
        $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
        $re['gbk'] = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
        $re['big5'] = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
        preg_match_all($re[$charset], $str, $match);
        $strlen = strlen($str);
        $slice = join("", array_slice($match[0], $start, $length));
    }
    if ($suffix && $strlen > $length)
        return $slice . "…";
    return $slice;
}

/**
 * 转换 HTML 特殊字符以及空格和换行符
 *
 * 一般将 <textarea> 标记中输入的内容从数据库中读出来后在网页中显示
 */
function toHtml($text) {
    $text = htmlspecialchars($text);
    $text = nl2br(str_replace(' ', '&nbsp;', $text));
    return $text;
}

/**
 * 过滤所有的JS，STYLE，HTML标记
 *
 * @param string $string
 * @return string
 */
function stripTag($string, $allowed_tags = null) {
    $string = preg_replace("/<script.*>.*<\/script>/isU", "", $string);
    $string = preg_replace("/<style.*>.*<\/style>/isU", "", $string);
    $string = strip_tags($string, $allowed_tags);
    return $string;
}

/**
 * 返回永远唯一32位md5的随机字符串
 */
function md5Rand() {
    return md5(uniqid(time() . rand()));
}

/**
 * 得到小写的文件扩展名,如".php"
 *
 * @param string $fileName :文件名
 * @param bool $dot 是否含点号
 * @return string
 */
function fileExt($fileName, $dot = true) {
    $pos = strripos($fileName, '.');
    if ($pos === false) {
        return '';
    }
    $ext = $dot ? substr($fileName, $pos) : substr($fileName, $pos + 1);
    if (false !== stripos($ext, '?')) {
        $ext = strchr($ext, '?', true);
    }
    return strtolower($ext);
}

/**
 * 获取当前毫秒数
 * @return float
 */
function millisecond() {
    list($msec, $sec) = explode(' ', microtime());
    return (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
}

/**
 * 缓存函数
 * @param string $type 缓存分类
 * @param string $key 缓存键
 * @param mixed $data 要缓存的数据，传如了该参数则写入缓存，该参数传null或不传，则为读取缓存
 * @param int $expire 缓存过期时间
 * @param bool $remove 是否删除缓存，传入该参数后$data，$expire参数失效
 * @return mixed 读取的缓存数据，或写入是否成功
 * @throws Exception
 */
function cache($type, $key, $data = null, $expire = 0, $remove = false) {
    $default_cache_type = Sys::getCfg('default_cache_type');
    if (!$default_cache_type) {
        $default_cache_type = 'file';
    }
    switch ($default_cache_type) {
        case 'file':
            return fcache($type, $key, $data, $expire, $remove);
            break;
        case 'apcu':
            return ucache($type, $key, $data, $expire, $remove);
            break;
        case 'redis':
            return rcache($type, $key, $data, $expire, $remove);
            break;
    }
    throw new Exception('未配置 default_cache_type');
}

/**
 * 文件缓存
 * @param string $dir 缓存目录
 * @param string $key 缓存键
 * @param mixed $data 要缓存的数据，传如了该参数则写入缓存，该参数传null或不传，则为读取缓存
 * @param int $expire 缓存过期时间
 * @param bool $remove 是否删除缓存，传入该参数后$data，$expire参数失效
 * @return mixed 读取的缓存数据，或写入是否成功
 * @throws Exception
 */
function fcache($dir, $key, $data = null, $expire = 0, $remove = false) {
    
    static $cache;
    
    if (!$cache) {
        
        $cache = new Cache();
    }
    
    $dir = trim($dir, '/');
    
    $dir = DATA_PATH . "/Cache/{$dir}/";
    
    if (!file_exists($dir)) {
        mkdir($dir, 0775, true);
    }
    
    $cache->setPath($dir);
    
    if ($remove) {
        return $cache->remove($key);
    }
    
    if ($data !== null) {
        return $cache->set($key, $data, $expire);
    } else {
        return $cache->get($key);
    }
}

/**
 * APCU缓存
 * @param string $type 缓存分类
 * @param string $key 缓存键
 * @param mixed $data 要缓存的数据，传如了该参数则写入缓存，该参数传null或不传，则为读取缓存
 * @param int $expire 缓存过期时间
 * @param bool $remove 是否删除缓存，传入该参数后$data，$expire参数失效
 * @return mixed 读取的缓存数据，或写入是否成功
 * @throws Exception
 */
function ucache($type, $key, $data = null, $expire = 0, $remove = false) {
    
    static $apcu;
    static $config;
    
    $config || $config = Sys::getCfg('apcu');
    
    if (!$apcu) {
        
        $apcu = new Apcu();
    }
    
    if ($config['prefix']) {
        $key = $config['prefix'] . $type . ':' . $key;
    } else {
        $key = $type . ':' . $key;
    }
    
    if ($remove) {
        return $apcu->remove($key);
    }
    
    if ($data !== null) {
        return $apcu->set($key, $data, $expire);
    } else {
        return $apcu->get($key);
    }
}

/**
 * redis缓存
 * @param string $type 缓存分类
 * @param string $key 缓存键
 * @param mixed $data 要缓存的数据，传如了该参数则写入缓存，该参数传null或不传，则为读取缓存
 * @param int $expire 缓存过期时间
 * @param bool $remove 是否删除缓存，传入该参数后$data，$expire参数失效
 * @return mixed 读取的缓存数据，或写入是否成功
 * @throws Exception
 */
function rcache($type, $key, $data = null, $expire = 0, $remove = false) {
    static $config;
    
    $config || $config = Sys::getCfg();
    
    $rdconfig = $config['redis']['master'];
    
    if ($rdconfig['prefix']) {
        $key = $rdconfig['prefix'] . $type . ':' . $key;
    } else {
        $key = $type . ':' . $key;
    }
    
    if ($remove) {
        try {
            Rd()->del($key);
        } catch (Exception $ex) {
            return false;
        }
        return true;
    }
    
    if ($data !== null) {
        $data = serialize($data);
        try {
            if ($expire === 0) {
                Rd()->set($key, $data);
            } else {
                Rd()->setex($key, $expire, $data);
            }
        } catch (Exception $ex) {
            return false;
        }
        
        return true;
    } else {
        try {
            $data = Rd()->get($key);
        } catch (Exception $ex) {
            return false;
        }
        
        if (!$data) {
            return false;
        } else {
            return unserialize($data);
        }
    }
}

/**
 * Redis HashTable 缓存
 * @param string $key
 * @param string $hashKey
 * @param mixed $value
 * @param int $remove 0 不删除，1 删除hashKey和value，2 删除key对应的整个hashtable
 * @return mixed
 */
function hcache($key, $hashKey, $value = null, $remove = 0) {
    static $config;
    
    $config || $config = Sys::getCfg();
    
    $rdconfig = $config['redis']['master'];
    
    if ($rdconfig['prefix']) {
        $key = $rdconfig['prefix'] . $key;
    }
    
    if ($remove === 2) {
        try {
            Rd()->del($key);
        } catch (Exception $ex) {
            return false;
        }
        return true;
    }
    
    if ($remove === 1) {
        try {
            Rd()->hDel($key, $hashKey);
        } catch (Exception $ex) {
            return false;
        }
        return true;
    }
    
    if ($value !== null) {
        
        $value = serialize($value);
        
        try {
            Rd()->hSet($key, $hashKey, $value);
        } catch (Exception $ex) {
            return false;
        }
        
        return true;
    } else {
        
        try {
            $value = Rd()->hGet($key, $hashKey);
        } catch (Exception $ex) {
            return null;
        }
        
        if (!$value) {
            return false;
        } else {
            return unserialize($value);
        }
    }
}

/**
 * 数据操作类
 * @return Mysql
 */
function Db($server = 'master') {
    
    static $db = array();
    static $config;
    
    $config || $config = Sys::getCfg();
    
    $dbconfig = $config['database'][$server];
    $dbconfig['display_error'] = $config['display_error'];
    
    if (!isset($db[$server])) {
        $db[$server] = new Mysql($dbconfig);
    }
    
    return $db[$server];
}

/**
 * Redis操作类
 * @return Redis
 */
function Rd($server = 'master') {
    
    static $rd = array();
    static $config;
    
    $config || $config = Sys::getCfg();
    
    $rdconfig = $config['redis'][$server];
    
    if (!isset($rd[$server])) {
        try {
            $rd[$server] = new Redis();
        } catch (Exception $ex) {
            $rd[$server] = null;
        }
    }
    
    $connected = false;
    
    try {
        if ($rd[$server]->ping()) {
            $connected = true;
        }
    } catch (Exception $ex) {
    
    }
    
    if (!$connected) {
        $rd[$server]->pconnect($rdconfig['host'], $rdconfig['port']);
        if ($rdconfig['password']) {
            $rd[$server]->auth($rdconfig['password']);
        }
    }
    
    return $rd[$server];
}

/**
 * 返回客户端IP
 *
 * @return string 失败返回字符串 unknown
 */
function clientip() {
    if (getenv("HTTP_CLIENT_IP") && strcasecmp(getenv("HTTP_CLIENT_IP"), "unknown"))
        $ip = getenv("HTTP_CLIENT_IP");
    else if (getenv("HTTP_X_FORWARDED_FOR") && strcasecmp(getenv("HTTP_X_FORWARDED_FOR"), "unknown"))
        $ip = getenv("HTTP_X_FORWARDED_FOR");
    else if (getenv("REMOTE_ADDR") && strcasecmp(getenv("REMOTE_ADDR"), "unknown"))
        $ip = getenv("REMOTE_ADDR");
    else if (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], "unknown"))
        $ip = $_SERVER['REMOTE_ADDR'];
    else
        $ip = "unknown";
    return ($ip);
}

/**
 * 过滤utf8mb4字符
 */
function filter_utf8mb4($str) {
    // 替换XML不支持的字符（不在XML允许范围内的字符）
    $str = preg_replace('/[^\x{1}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]/u', '', $str);
    // 替换XML中定义的控制符
    $str = preg_replace('/[\x{1}-\x{8}\x{B}-\x{C}\x{E}-\x{1F}\x{7F}-\x{84}\x{86}-\x{9F}]/u', '', $str);
    // 替换unicode未定义支付
    $str = preg_replace('/[\x{FDD0}-\x{FDDF}\x{1FFFE}-\x{1FFFF}\x{2FFFE}-\x{2FFFF}\x{3FFFE}-\x{3FFFF}\x{4FFFE}-\x{4FFFF}\x{5FFFE}-\x{5FFFF}\x{6FFFE}-\x{6FFFF}\x{7FFFE}-\x{7FFFF}\x{8FFFE}-\x{8FFFF}\x{9FFFE}-\x{9FFFF}\x{AFFFE}-\x{AFFFF}\x{BFFFE}-\x{BFFFF}\x{CFFFE}-\x{CFFFF}\x{DFFFE}-\x{DFFFF}\x{EFFFE}-\x{EFFFF}\x{FFFFE}-\x{FFFFF}\x{10FFFE}-\x{10FFFF}]/u', '', $str);
    // 替换unicode中日韩兼容字符
    $str = preg_replace('/[\x{F900}-\x{FAFF}\x{2F800}-\x{2FA1F}]/u', '', $str);
    // 替换等号开头，在Excel中会被认为是公式
    $str = rtrim(trim($str), '=');
    
    // 替换utf8扩展字符（0xFFFF以上的字符）
    return preg_replace_callback('/./u', function (array $match) {
        return strlen($match[0]) >= 4 ? '' : $match[0];
    }, $str);
}

/**
 * unicode编码
 */
function unicode_encode($string) {
    $string = iconv('UTF-8', 'UCS-2', $string);
    $len = strlen($string);
    $str = '';
    for ($i = 0; $i < $len - 1; $i = $i + 2) {
        $c = $string[$i];
        $c2 = $string[$i + 1];
        if (ord($c) > 0) {    // 两个字节的文字
            $str .= '\u' . base_convert(ord($c), 10, 16) . base_convert(ord($c2), 10, 16);
        } else {
            $str .= $c2;
        }
    }
    return $str;
}

/**
 * unicode解码
 */
function unicode_decode($string) {
    $pattern = '/([\w]+)|(\\\u([\w]{4}))/i';
    preg_match_all($pattern, $string, $matches);
    if (!empty($matches)) {
        $string = '';
        for ($j = 0; $j < count($matches[0]); $j++) {
            $str = $matches[0][$j];
            if (strpos($str, '\\u') === 0) {
                $code = base_convert(substr($str, 2, 2), 16, 10);
                $code2 = base_convert(substr($str, 4), 16, 10);
                $c = chr($code) . chr($code2);
                $c = iconv('UCS-2', 'UTF-8', $c);
                $string .= $c;
            } else {
                $string .= $str;
            }
        }
    }
    return $string;
}

/**
 * base58编码
 *
 * 编码方式速度慢于base64_encode
 * 生成的编码不含视觉上易被混淆的字母和数字，且没有+=等特殊字符，对url友好
 * 作为url参数传递时不需要urlencode，接收时也不需要urldecode
 *
 * @param $string
 * @return false|string
 */
function base58_encode($string) {
    $alphabet = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
    $base = strlen($alphabet);
    if (is_string($string) === false) {
        return false;
    }
    if (strlen($string) === 0) {
        return '';
    }
    $bytes = array_values(unpack('C*', $string));
    $decimal = $bytes[0];
    for ($i = 1, $l = count($bytes); $i < $l; $i++) {
        $decimal = bcmul($decimal, 256);
        $decimal = bcadd($decimal, $bytes[$i]);
    }
    $output = '';
    while ($decimal >= $base) {
        $div = bcdiv($decimal, $base, 0);
        $mod = bcmod($decimal, $base);
        $output .= $alphabet[$mod];
        $decimal = $div;
    }
    if ($decimal > 0) {
        $output .= $alphabet[$decimal];
    }
    $output = strrev($output);
    foreach ($bytes as $byte) {
        if ($byte === 0) {
            $output = $alphabet[0] . $output;
            continue;
        }
        break;
    }
    return (string)$output;
}

function base58_decode($base58) {
    $alphabet = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
    $base = strlen($alphabet);
    if (is_string($base58) === false) {
        return false;
    }
    if (strlen($base58) === 0) {
        return '';
    }
    $indexes = array_flip(str_split($alphabet));
    $chars = str_split($base58);
    foreach ($chars as $char) {
        if (isset($indexes[$char]) === false) {
            return false;
        }
    }
    $decimal = $indexes[$chars[0]];
    for ($i = 1, $l = count($chars); $i < $l; $i++) {
        $decimal = bcmul($decimal, $base);
        $decimal = bcadd($decimal, $indexes[$chars[$i]]);
    }
    $output = '';
    while ($decimal > 0) {
        $byte = bcmod($decimal, 256);
        $output = pack('C', $byte) . $output;
        $decimal = bcdiv($decimal, 256, 0);
    }
    foreach ($chars as $char) {
        if ($indexes[$char] === 0) {
            $output = "\x00" . $output;
            continue;
        }
        break;
    }
    return $output;
}

/**
 * 加强版md5加密，保障秘钥不会被碰撞解析
 *
 * @param $string
 * @param $key
 * @return string
 */
function cmd5($string, $key) {
    $password = md5($string . $key);
    $password1 = substr($password, 0, 16);
    $password2 = substr($password, 16, 16);
    return $password1 . substr(md5($password2), 0, 16);
}